use axum::{
    extract::{Path, State},
    http::{header, Method, StatusCode},
    response::{IntoResponse, Json, Response},
};
use serde::Serialize;

use crate::error::AppError;
use crate::utils::DvcAuthenticatedUser;

#[derive(Debug, Serialize)]
pub struct DvcFileInfo {
    pub path: String,
    pub md5: String,
    pub size: i64,
    pub exists: bool,
}

// DVC remote endpoint for push operations
pub async fn handle_dvc_request(
    method: Method,
    Path(path_parts): Path<Vec<String>>,
    DvcAuthenticatedUser(mut user): DvcAuthenticatedUser,
    State(state): State<crate::AppState>,
    body: axum::body::Bytes,
) -> Result<Response, AppError> {
    // Resolve user from placeholder if needed
    if user.id == 0 {
        user = state.auth_service.get_user_by_email(&user.email).await
            .map_err(|e| AppError::Auth(format!("Failed to resolve user: {}", e)))?;
    }
    match method.as_str() {
        "GET" => handle_dvc_get(path_parts, DvcAuthenticatedUser(user), state).await,
        "POST" => handle_dvc_put(path_parts, DvcAuthenticatedUser(user), state, body).await, // DVC uses POST for uploads
        "PUT" => handle_dvc_put(path_parts, DvcAuthenticatedUser(user), state, body).await,
        "DELETE" => handle_dvc_delete(path_parts, DvcAuthenticatedUser(user), state).await,
        "HEAD" => handle_dvc_head(path_parts, DvcAuthenticatedUser(user), state).await,
        _ => Err(AppError::NotFound("Method not allowed".to_string())),
    }
}

async fn handle_dvc_get(
    path_parts: Vec<String>,
    mut user: DvcAuthenticatedUser,
    state: crate::AppState,
) -> Result<Response, AppError> {
    // Resolve user from placeholder if needed
    if user.0.id == 0 {
        user.0 = state.auth_service.get_user_by_email(&user.0.email).await
            .map_err(|e| AppError::Auth(format!("Failed to resolve user: {}", e)))?;
    }
    if path_parts.is_empty() {
        return Err(AppError::BadRequest("Invalid path".to_string()));
    }

    let path = path_parts.join("/");

    // Check if this looks like a cache file path (hash pattern like "5b/795996..." or "5b795996...")
    let potential_hash = path.replace('/', "");
    if potential_hash.len() == 32 && potential_hash.chars().all(|c| c.is_ascii_hexdigit()) {
        // This looks like a hash, try to serve it as a cache file
        match state
            .storage_service
            .get_data_file_by_hash(&potential_hash)
            .await
        {
            Ok(file_data) => {
                return Ok((
                    StatusCode::OK,
                    [(header::CONTENT_TYPE, "application/octet-stream")],
                    [(header::CONTENT_LENGTH, file_data.len().to_string())],
                    [(header::ETAG, format!("\"{}\"", potential_hash))],
                    file_data,
                )
                    .into_response());
            }
            Err(_) => {
                return Err(AppError::NotFound("Cache file not found".to_string()));
            }
        }
    }

    let filename = std::path::Path::new(&path)
        .file_name()
        .and_then(|n| n.to_str())
        .unwrap_or("data");

    // Check if this is a DVC metadata file (.dvc extension)
    if filename.ends_with(".dvc") {
        // Try to get DVC metadata file
        let file_data = state
            .storage_service
            .get_dvc_metadata(filename)
            .await
            .map_err(|_| AppError::NotFound("DVC metadata file not found".to_string()))?;

        return Ok((
            StatusCode::OK,
            [(header::CONTENT_TYPE, "text/yaml")],
            [(
                header::CONTENT_DISPOSITION,
                format!("attachment; filename=\"{}\"", filename),
            )],
            file_data,
        )
            .into_response());
    }

    // For data files, try to find by name using the new method
    if let Some(item) = state
        .data_service
        .get_data_item_by_name(filename, user.0.id)
        .await
        .map_err(|e| AppError::Internal(format!("Failed to get data item by name: {}", e)))?
    {
        let file_data = if let Some(hash) = &item.file_hash {
            // Use DVC cache for content-addressed storage
            state
                .storage_service
                .get_data_file_by_hash(hash)
                .await
                .map_err(|e| {
                    AppError::Internal(format!("Failed to get data file from cache: {}", e))
                })?
        } else {
            // Fallback to direct file path
            state
                .storage_service
                .get_file(&item.file_path)
                .await
                .map_err(|e| AppError::Internal(format!("Failed to get data file: {}", e)))?
        };

        Ok((
            StatusCode::OK,
            [(header::CONTENT_TYPE, "application/octet-stream")],
            [(
                header::CONTENT_DISPOSITION,
                format!("attachment; filename=\"{}\"", filename),
            )],
            file_data,
        )
            .into_response())
    } else {
        Err(AppError::NotFound("File not found".to_string()))
    }
}

async fn handle_dvc_put(
    path_parts: Vec<String>,
    mut user: DvcAuthenticatedUser,
    state: crate::AppState,
    body: axum::body::Bytes,
) -> Result<Response, AppError> {
    // Resolve user from placeholder if needed
    if user.0.id == 0 {
        user.0 = state.auth_service.get_user_by_email(&user.0.email).await
            .map_err(|e| AppError::Auth(format!("Failed to resolve user: {}", e)))?;
    }
    use crate::utils::hash::calculate_file_hash;

    if path_parts.is_empty() {
        return Err(AppError::BadRequest("Invalid path".to_string()));
    }

    let path = path_parts.join("/");

    if body.is_empty() {
        return Err(AppError::BadRequest("Empty file content".to_string()));
    }

    // Check if this is a DVC cache file path (content-addressed storage)
    if path.starts_with("files/md5/") || path.starts_with("files/sha256/") {
        return handle_dvc_cache_put(&path, body, user, state).await;
    }

    // Calculate file hash for regular uploads
    let file_hash = calculate_file_hash(&body)?;

    // Extract filename from path
    let filename = std::path::Path::new(&path)
        .file_name()
        .and_then(|n| n.to_str())
        .unwrap_or("dvc_file");

    // Check if this is a DVC metadata file (.dvc extension)
    if filename.ends_with(".dvc") {
        // Handle DVC metadata file using unified workflow
        return handle_dvc_metadata_upload(filename, &body, user, state).await;
    }

    // Handle data file using proper DVC workflow validation

    // 1. Check if data item already exists globally (duplicate prevention)
    if let Some(existing_item) = state
        .data_service
        .get_data_item_by_hash(&file_hash)
        .await
        .map_err(|e| AppError::Internal(format!("Failed to check for existing data item: {}", e)))?
    {
        println!(
            "DEBUG: DVC push found existing data item {} with hash {}, skipping",
            existing_item.id, file_hash
        );
        return Ok(StatusCode::OK.into_response());
    }

    // 2. Check if DVC metadata exists for this hash using new table (workflow validation)
    if let Some(_dvc_metadata) = state
        .dvc_metadata_service
        .get_dvc_metadata_by_data_hash_and_user(&file_hash, user.0.id)
        .await
        .map_err(|e| AppError::Internal(format!("Failed to check DVC metadata existence: {}", e)))?
    {
        // 3. Store data file in DVC cache
        let _stored_hash = state
            .storage_service
            .store_data_file(&body)
            .await
            .map_err(|e| {
                AppError::Internal(format!("Failed to store data file in DVC cache: {}", e))
            })?;

        // 4. Create data item record for the uploaded file
        use crate::models::NewDataItem;
        let data_item = state
            .data_service
            .create_data_item(NewDataItem {
                name: filename.to_string(),
                description: Some(format!("DVC tracked file: {}", path)),
                project: None,
                source: "DVC Push".to_string(),
                file_path: format!("cache/{}/{}", &file_hash[0..2], &file_hash[2..]),
                file_hash: Some(file_hash.clone()),
                file_size: Some(body.len() as i64),
                file_type: None,
                is_folder: false,
                file_count: None,
                user_id: user.0.id,
                parent_id: None,
                is_public: true, // DVC files are public by default
            })
            .await
            .map_err(|e| AppError::Internal(format!("Failed to create data item: {}", e)))?;

        println!(
            "DEBUG: DVC push successfully stored file with hash {} and created data item {}",
            file_hash, data_item.id
        );

        Ok(StatusCode::OK.into_response())
    } else {
        // No DVC metadata found - user forgot to run git commit
        return Err(AppError::BadRequest(
            format!("No DVC metadata found for file hash {}. Did you forget to run 'git commit' before 'dvc push'?\n\nRequired workflow:\n1. Add/modify files\n2. Run 'dvc add <file>' to create .dvc files\n3. Run 'git add <file>.dvc' and 'git commit' to upload metadata\n4. Run 'dvc push' to upload actual data", file_hash)
        ));
    }
}

async fn handle_dvc_cache_put(
    path: &str,
    body: axum::body::Bytes,
    mut user: DvcAuthenticatedUser,
    state: crate::AppState,
) -> Result<Response, AppError> {
    // Resolve user from placeholder if needed
    if user.0.id == 0 {
        user.0 = state.auth_service.get_user_by_email(&user.0.email).await
            .map_err(|e| AppError::Auth(format!("Failed to resolve user: {}", e)))?;
    }
    // Extract hash from path - handle both formats:
    // - files/md5/ff8b135f8e848c4afedabcda6ee8e2a0
    let path_parts: Vec<&str> = path.split('/').collect();
    let file_hash = if path_parts.len() >= 3 && path_parts[0] == "files" {
        // Reconstruct hash from path parts
        let mut hash_parts = Vec::new();
        for part in path_parts.iter().skip(2) {
            // Skip "files" and "md5"/"sha256"
            hash_parts.push(*part);
        }
        hash_parts.join("")
    } else {
        return Err(AppError::BadRequest(
            "Invalid cache path format".to_string(),
        ));
    };

    // Validate hash format (should be 32 hex characters for MD5)
    if file_hash.len() != 32 || !file_hash.chars().all(|c| c.is_ascii_hexdigit()) {
        return Err(AppError::BadRequest(
            "Invalid hash format in path".to_string(),
        ));
    }

    // Check if file already exists in cache (global duplicate prevention)
    if state
        .storage_service
        .data_file_exists(&file_hash)
        .await
        .map_err(|e| AppError::Internal(format!("Failed to check cache file existence: {}", e)))?
    {
        println!(
            "DEBUG: Cache file with hash {} already exists, skipping upload",
            file_hash
        );
        return Ok(StatusCode::OK.into_response());
    }

    // Store file in cache
    let _stored_hash = state
        .storage_service
        .store_data_file(&body)
        .await
        .map_err(|e| {
            AppError::Internal(format!("Failed to store data file in DVC cache: {}", e))
        })?;

    println!(
        "DEBUG: Successfully stored cache file with hash {}",
        file_hash
    );

    // Check if data item already exists globally (duplicate prevention)
    if let Some(_existing_item) = state
        .data_service
        .get_data_item_by_hash(&file_hash)
        .await
        .map_err(|e| AppError::Internal(format!("Failed to check for existing data item: {}", e)))?
    {
        println!(
            "DEBUG: Data item with hash {} already exists, skipping creation",
            file_hash
        );
        return Ok(StatusCode::OK.into_response());
    }

    // Find DVC metadata for this hash to get the original filename
    if let Some(dvc_metadata) = state
        .dvc_metadata_service
        .get_dvc_metadata_by_data_hash_and_user(&file_hash, user.0.id)
        .await
        .map_err(|e| AppError::Internal(format!("Failed to check DVC metadata existence: {}", e)))?
    {
        // Use the data filename from DVC metadata
        let original_filename = &dvc_metadata.data_filename;

        // Create data item record for the uploaded file
        use crate::models::NewDataItem;
        let data_item = state
            .data_service
            .create_data_item(NewDataItem {
                name: original_filename.to_string(),
                description: Some(format!("DVC tracked file: {}", dvc_metadata.data_filename)),
                project: dvc_metadata.project.clone(),
                source: "DVC Push".to_string(),
                file_path: format!("cache/{}/{}", &file_hash[0..2], &file_hash[2..]),
                file_hash: Some(file_hash.clone()),
                file_size: Some(body.len() as i64),
                file_type: None,
                is_folder: false,
                file_count: None,
                user_id: user.0.id,
                parent_id: None,
                is_public: true, // DVC files are public by default
            })
            .await
            .map_err(|e| AppError::Internal(format!("Failed to create data item: {}", e)))?;

        println!(
            "DEBUG: DVC cache push successfully created data item {} for file {}",
            data_item.id, original_filename
        );
    } else {
        println!(
            "DEBUG: No DVC metadata found for hash {}, cannot create data item",
            file_hash
        );
    }

    Ok(StatusCode::OK.into_response())
}

async fn handle_dvc_metadata_upload(
    filename: &str,
    body: &axum::body::Bytes,
    mut user: DvcAuthenticatedUser,
    state: crate::AppState,
) -> Result<Response, AppError> {
    // Resolve user from placeholder if needed
    if user.0.id == 0 {
        user.0 = state.auth_service.get_user_by_email(&user.0.email).await
            .map_err(|e| AppError::Auth(format!("Failed to resolve user: {}", e)))?;
    }
    use crate::utils::{
        dvc::{extract_dependencies_from_dvc, extract_outputs_from_dvc, parse_dvc_file},
        hash::calculate_file_hash,
    };

    // Parse DVC content to get actual file info
    let dvc_content = String::from_utf8(body.to_vec())
        .map_err(|e| AppError::Internal(format!("Failed to parse DVC content as UTF-8: {}", e)))?;

    let metadata = parse_dvc_file(&dvc_content)
        .map_err(|e| AppError::Internal(format!("Failed to parse DVC metadata: {}", e)))?;

    if let Some(_output) = metadata.outs.first() {
        // Store DVC metadata file
        let _dvc_path = state
            .storage_service
            .store_dvc_metadata(filename, &dvc_content)
            .await
            .map_err(|e| AppError::Internal(format!("Failed to store DVC metadata: {}", e)))?;

        // Check for existing DVC metadata to prevent duplicates
        if let Some(existing_dvc) = state
            .dvc_metadata_service
            .get_dvc_metadata_by_filename_and_user(filename, user.0.id)
            .await
            .map_err(|e| {
                AppError::Internal(format!("Failed to check for existing DVC metadata: {}", e))
            })?
        {
            println!("DEBUG: DVC metadata upload found existing DVC metadata {} with filename {}, skipping creation",
                     existing_dvc.id, filename);
            return Ok(StatusCode::OK.into_response());
        }

        // Create DVC metadata record with content
        let _dvc_hash = calculate_file_hash(dvc_content.as_bytes())?;
        let dvc_metadata = state
            .dvc_metadata_service
            .create_dvc_metadata_with_content(user.0.id, &filename.to_string(), Some(&dvc_content))
            .await
            .map_err(|e| AppError::Internal(format!("Failed to create DVC metadata: {}", e)))?;

        println!(
            "DEBUG: DVC metadata upload created DVC metadata record: dvc={}",
            dvc_metadata.id
        );

        // NEW: ENHANCED LINEAGE CAPTURE
        // Extract dependencies and outputs from DVC file
        let dependencies = extract_dependencies_from_dvc(&dvc_content)
            .map_err(|e| AppError::Internal(format!("Failed to extract dependencies: {}", e)))?;
        let outputs = extract_outputs_from_dvc(&dvc_content)
            .map_err(|e| AppError::Internal(format!("Failed to extract outputs: {}", e)))?;

        // If we have dependencies, create lineage relationships
        if !dependencies.is_empty() {
            for output_path in &outputs {
                // Find the data item for the output file
                if let Some(output_item) = state
                    .data_service
                    .get_data_item_by_name(
                        std::path::Path::new(output_path)
                            .file_name()
                            .and_then(|n| n.to_str())
                            .unwrap_or(output_path),
                        user.0.id,
                    )
                    .await
                    .map_err(|e| {
                        AppError::Internal(format!("Failed to get output data item: {}", e))
                    })?
                {
                    // For each dependency, find the corresponding data item and create lineage
                    for dep_path in &dependencies {
                        if let Some(dep_item) = state
                            .data_service
                            .get_data_item_by_name(
                                std::path::Path::new(dep_path)
                                    .file_name()
                                    .and_then(|n| n.to_str())
                                    .unwrap_or(dep_path),
                                user.0.id,
                            )
                            .await
                            .map_err(|e| {
                                AppError::Internal(format!(
                                    "Failed to get dependency data item: {}",
                                    e
                                ))
                            })?
                        {
                            // Create lineage relationship
                            // TODO: Implement with SeaORM
                            let _lineage_created = state
                                .lineage_service
                                .create_lineage(crate::models::NewDataLineage {
                                    child_id: output_item.id,
                                    parent_id: dep_item.id,
                                    relationship_type: "derived_from".to_string(),
                                    confidence: 1.0,
                                    detection_method: "dvc_pipeline".to_string(),
                                    description: Some(format!(
                                        "Lineage captured from DVC file: {}",
                                        filename
                                    )),
                                    created_by: user.0.id,
                                })
                                .await?;

                            println!(
                                "DEBUG: Created DVC lineage: {} -> {}",
                                dep_item.name, output_item.name
                            );
                        }
                    }
                }
            }
        } else {
            // No dependencies found, trigger auto-detection for this item
            for output_path in &outputs {
                if let Some(output_item) = state
                    .data_service
                    .get_data_item_by_name(
                        std::path::Path::new(output_path)
                            .file_name()
                            .and_then(|n| n.to_str())
                            .unwrap_or(output_path),
                        user.0.id,
                    )
                    .await
                    .map_err(|e| {
                        AppError::Internal(format!("Failed to get output data item: {}", e))
                    })?
                {
                    // TODO: Implement intelligent lineage detection
                    // let suggestions = state.storage_service.auto_detect_lineage(output_item.id).await
                    //     .map_err(|e| AppError::Internal(format!("Failed to auto-detect lineage: {}", e)))?;

                    println!(
                        "DEBUG: Lineage detection temporarily disabled for {}",
                        output_item.name
                    );
                }
            }
        }

        return Ok(StatusCode::OK.into_response());
    }

    Err(AppError::BadRequest(
        "Invalid DVC file format - no outputs found".to_string(),
    ))
}

async fn handle_dvc_delete(
    path_parts: Vec<String>,
    mut user: DvcAuthenticatedUser,
    state: crate::AppState,
) -> Result<Response, AppError> {
    // Resolve user from placeholder if needed
    if user.0.id == 0 {
        user.0 = state.auth_service.get_user_by_email(&user.0.email).await
            .map_err(|e| AppError::Auth(format!("Failed to resolve user: {}", e)))?;
    }
    if path_parts.is_empty() {
        return Err(AppError::BadRequest("Invalid path".to_string()));
    }

    let path = path_parts.join("/");
    let filename = std::path::Path::new(&path)
        .file_name()
        .and_then(|n| n.to_str())
        .unwrap_or("data");

    // Check if this is a DVC metadata file (.dvc extension)
    if filename.ends_with(".dvc") {
        // Find and delete DVC metadata item
        let items = state
            .data_service
            .list_data_items(user.0.id, None, None)
            .await
            .map_err(|e| AppError::Internal(format!("Failed to list data items: {}", e)))?;

        if let Some(item) = items.0.iter().find(|item| item.name == filename) {
            let deleted = state
                .data_service
                .delete_data_item(item.id, user.0.id)
                .await?;
            if deleted {
                // Delete DVC metadata file
                let _ = state.storage_service.delete_file(&item.file_path).await;
                return Ok(StatusCode::NO_CONTENT.into_response());
            }
        }
        return Err(AppError::NotFound(
            "DVC metadata file not found".to_string(),
        ));
    }

    // For data files, try to find by name using the new method
    if let Some(item) = state
        .data_service
        .get_data_item_by_name(filename, user.0.id)
        .await
        .map_err(|e| AppError::Internal(format!("Failed to get data item by name: {}", e)))?
    {
        let deleted = state
            .data_service
            .delete_data_item(item.id, user.0.id)
            .await?;

        if deleted {
            // Delete file from storage (if not in cache, as cache is shared)
            if !item.file_path.starts_with("cache/") {
                let _ = state.storage_service.delete_file(&item.file_path).await;
            }
            Ok(StatusCode::NO_CONTENT.into_response())
        } else {
            Err(AppError::NotFound("File not found".to_string()))
        }
    } else {
        Err(AppError::NotFound("File not found".to_string()))
    }
}

async fn handle_dvc_head(
    path_parts: Vec<String>,
    mut user: DvcAuthenticatedUser,
    state: crate::AppState,
) -> Result<Response, AppError> {
    // Resolve user from placeholder if needed
    if user.0.id == 0 {
        user.0 = state.auth_service.get_user_by_email(&user.0.email).await
            .map_err(|e| AppError::Auth(format!("Failed to resolve user: {}", e)))?;
    }
    if path_parts.is_empty() {
        return Err(AppError::BadRequest("Invalid path".to_string()));
    }

    let path = path_parts.join("/");

    // Check if this looks like a cache file path (hash pattern like "5b/795996..." or "5b795996...")
    let potential_hash = path.replace('/', "");
    if potential_hash.len() == 32 && potential_hash.chars().all(|c| c.is_ascii_hexdigit()) {
        // This looks like a hash, check if cache file exists
        match state
            .storage_service
            .data_file_exists(&potential_hash)
            .await
        {
            Ok(true) => {
                // Get file size for HEAD response
                let cache_path = format!(
                    "{}/cache/{}/{}",
                    state.storage_service.base_path,
                    &potential_hash[0..2],
                    &potential_hash[2..]
                );
                if let Ok(metadata) = tokio::fs::metadata(&cache_path).await {
                    return Ok((
                        StatusCode::OK,
                        [(header::CONTENT_LENGTH, metadata.len().to_string())],
                        [(header::ETAG, format!("\"{}\"", potential_hash))],
                    )
                        .into_response());
                }
            }
            Ok(false) => {
                return Err(AppError::NotFound("Cache file not found".to_string()));
            }
            Err(e) => {
                return Err(AppError::Internal(format!(
                    "Failed to check cache file existence: {}",
                    e
                )));
            }
        }
    }

    let filename = std::path::Path::new(&path)
        .file_name()
        .and_then(|n| n.to_str())
        .unwrap_or("data");

    // Check if this is a DVC metadata file (.dvc extension)
    if filename.ends_with(".dvc") {
        // Check if DVC metadata file exists
        let exists = state
            .storage_service
            .dvc_metadata_exists(filename)
            .await
            .map_err(|e| {
                AppError::Internal(format!("Failed to check DVC metadata existence: {}", e))
            })?;

        if exists {
            let metadata_path =
                format!("{}/metadata/{}", state.storage_service.base_path, filename);
            if let Ok(metadata) = tokio::fs::metadata(&metadata_path).await {
                let hash = crate::utils::hash::calculate_file_hash(
                    &tokio::fs::read(&metadata_path).await.unwrap_or_default(),
                )
                .unwrap_or_default();
                return Ok((
                    StatusCode::OK,
                    [(header::CONTENT_LENGTH, metadata.len().to_string())],
                    [(header::ETAG, format!("\"{}\"", hash))],
                )
                    .into_response());
            }
        }
        return Err(AppError::NotFound(
            "DVC metadata file not found".to_string(),
        ));
    }

    // For data files, try to find by name using the new method
    if let Some(item) = state
        .data_service
        .get_data_item_by_name(filename, user.0.id)
        .await
        .map_err(|e| AppError::Internal(format!("Failed to get data item by name: {}", e)))?
    {
        Ok((
            StatusCode::OK,
            [(
                header::CONTENT_LENGTH,
                item.file_size.unwrap_or(0).to_string(),
            )],
            [(
                header::ETAG,
                format!("\"{}\"", item.file_hash.unwrap_or_default()),
            )],
        )
            .into_response())
    } else {
        Err(AppError::NotFound("File not found".to_string()))
    }
}

// DVC API endpoints for integration
pub async fn dvc_list_files(
    mut user: DvcAuthenticatedUser,
    State(state): State<crate::AppState>,
) -> Result<Json<Vec<DvcFileInfo>>, AppError> {
    // Resolve user from placeholder if needed
    if user.0.id == 0 {
        user.0 = state.auth_service.get_user_by_email(&user.0.email).await
            .map_err(|e| AppError::Auth(format!("Failed to resolve user: {}", e)))?;
    }
    let _user_id = user.0.id;
    let (items, _) = state
        .data_service
        .list_data_items(user.0.id, None, None)
        .await?;

    let file_info: Vec<DvcFileInfo> = items
        .into_iter()
        .map(|item| {
            // For unified workflow, use the item name as the path
            // DVC metadata files will have .dvc extension
            let path = if item.name.ends_with(".dvc") {
                item.name.clone()
            } else {
                // For data files, just use the filename
                item.name.clone()
            };

            DvcFileInfo {
                path,
                md5: item.file_hash.unwrap_or_default(),
                size: item.file_size.unwrap_or(0),
                exists: true,
            }
        })
        .collect();

    Ok(Json(file_info))
}

pub async fn dvc_get_file_info(
    Path(path): Path<String>,
    mut user: DvcAuthenticatedUser,
    State(state): State<crate::AppState>,
) -> Result<Json<DvcFileInfo>, AppError> {
    // Resolve user from placeholder if needed
    if user.0.id == 0 {
        user.0 = state.auth_service.get_user_by_email(&user.0.email).await
            .map_err(|e| AppError::Auth(format!("Failed to resolve user: {}", e)))?;
    }
    let filename = std::path::Path::new(&path)
        .file_name()
        .and_then(|n| n.to_str())
        .unwrap_or(&path);

    // Check if this is a DVC metadata file (.dvc extension)
    if filename.ends_with(".dvc") {
        // Check if DVC metadata file exists
        let exists = state
            .storage_service
            .dvc_metadata_exists(filename)
            .await
            .map_err(|e| {
                AppError::Internal(format!("Failed to check DVC metadata existence: {}", e))
            })?;

        if exists {
            let metadata_path =
                format!("{}/metadata/{}", state.storage_service.base_path, filename);
            if let Ok(metadata) = tokio::fs::metadata(&metadata_path).await {
                let hash = crate::utils::hash::calculate_file_hash(
                    &tokio::fs::read(&metadata_path).await.unwrap_or_default(),
                )
                .unwrap_or_default();
                return Ok(Json(DvcFileInfo {
                    path: filename.to_string(),
                    md5: hash,
                    size: metadata.len() as i64,
                    exists: true,
                }));
            }
        }
        return Err(AppError::NotFound(
            "DVC metadata file not found".to_string(),
        ));
    }

    // For data files, try to find by name using the new method
    if let Some(item) = state
        .data_service
        .get_data_item_by_name(filename, user.0.id)
        .await
        .map_err(|e| AppError::Internal(format!("Failed to get data item by name: {}", e)))?
    {
        Ok(Json(DvcFileInfo {
            path: filename.to_string(),
            md5: item.file_hash.clone().unwrap_or_else(|| "".to_string()),
            size: item.file_size.unwrap_or(0),
            exists: true,
        }))
    } else {
        Err(AppError::NotFound("File not found".to_string()))
    }
}

// DVC cache file serving endpoint
pub async fn handle_dvc_cache_get(
    method: Method,
    Path(hash_path): Path<String>,
    DvcAuthenticatedUser(_user): DvcAuthenticatedUser,
    State(state): State<crate::AppState>,
) -> Result<Response, AppError> {
    match method.as_str() {
        "GET" => handle_cache_file_get(hash_path, state).await,
        "HEAD" => handle_cache_file_head(hash_path, state).await,
        _ => Err(AppError::NotFound("Method not allowed".to_string())),
    }
}

async fn handle_cache_file_get(
    hash_path: String,
    state: crate::AppState,
) -> Result<Response, AppError> {
    // Extract hash from path - could be "5b/795996..." or just "5b795996..."
    let hash = if hash_path.contains('/') {
        hash_path.replace('/', "")
    } else {
        hash_path
    };

    // Validate hash format (should be 32 hex characters for MD5)
    if hash.len() != 32 {
        return Err(AppError::BadRequest("Invalid hash format".to_string()));
    }

    // Get file from cache
    let file_data = state
        .storage_service
        .get_data_file_by_hash(&hash)
        .await
        .map_err(|e| AppError::Internal(format!("Failed to get cache file: {}", e)))?;

    Ok((
        StatusCode::OK,
        [(header::CONTENT_TYPE, "application/octet-stream")],
        [(header::CONTENT_LENGTH, file_data.len().to_string())],
        [(header::ETAG, format!("\"{}\"", hash))],
        file_data,
    )
        .into_response())
}

async fn handle_cache_file_head(
    hash_path: String,
    state: crate::AppState,
) -> Result<Response, AppError> {
    // Extract hash from path
    let hash = if hash_path.contains('/') {
        hash_path.replace('/', "")
    } else {
        hash_path
    };

    // Validate hash format (should be 32 hex characters for MD5)
    if hash.len() != 32 {
        return Err(AppError::BadRequest("Invalid hash format".to_string()));
    }

    // Check if file exists in cache
    let exists = state
        .storage_service
        .data_file_exists(&hash)
        .await
        .map_err(|e| AppError::Internal(format!("Failed to check cache file existence: {}", e)))?;

    if exists {
        // Get file size for HEAD response
        let cache_path = format!(
            "{}/cache/{}/{}",
            state.storage_service.base_path,
            &hash[0..2],
            &hash[2..]
        );
        if let Ok(metadata) = tokio::fs::metadata(&cache_path).await {
            Ok((
                StatusCode::OK,
                [(header::CONTENT_LENGTH, metadata.len().to_string())],
                [(header::ETAG, format!("\"{}\"", hash))],
            )
                .into_response())
        } else {
            Err(AppError::NotFound("Cache file not found".to_string()))
        }
    } else {
        Err(AppError::NotFound("Cache file not found".to_string()))
    }
}
